#ifndef _NGX_NGIM_H_INCLUDED_
#define _NGX_NGIM_H_INCLUDED_

#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_event.h>
#include <ngx_event_connect.h>
#include <ngx_http.h>
#include <curl/curl.h>
#include "cjson/cJSON.h"


/* define */
#define NGX_NGIM_VERSION                1

#define NGX_LOG_DEBUG_NGIM              NGX_LOG_DEBUG_CORE

#define NGX_NGIM_WHICH_ALL          0x01
#define NGX_NGIM_WHICH_RND          0x02
#define NGX_NGIM_WHICH_IDX          0x04

#define NGX_NGIM_ASYNC_SYNC         0x01
#define NGX_NGIM_ASYNC_ASYN         0x02

#define NGX_NGIM_MODULE                 0x504D5453     /* "NGIM" */

#define NGX_NGIM_MAIN_CONF              0x00200000
#define NGX_NGIM_SRV_CONF               0x00400000
#define NGX_NGIM_TASK_CONF              0x00800000
#define NGX_NGIM_REC_CONF               0x01000000


#define NGX_NGIM_MAIN_CONF_OFFSET  offsetof(ngx_ngim_conf_ctx_t, main_conf)
#define NGX_NGIM_SRV_CONF_OFFSET   offsetof(ngx_ngim_conf_ctx_t, srv_conf)
#define NGX_NGIM_TASK_CONF_OFFSET  offsetof(ngx_ngim_conf_ctx_t, task_conf)


#define ngx_ngim_get_module_ctx(s, module)     (s)->ctx[module.ctx_index]
#define ngx_ngim_set_ctx(s, c, module)         s->ctx[module.ctx_index] = c;
#define ngx_ngim_delete_ctx(s, module)         s->ctx[module.ctx_index] = NULL;


#define ngx_ngim_get_module_main_conf(s, module)                             \
    (s)->main_conf[module.ctx_index]
#define ngx_ngim_get_module_srv_conf(s, module)  (s)->srv_conf[module.ctx_index]
#define ngx_ngim_get_module_task_conf(s, module)  ((s)->task_conf ? \
    (s)->task_conf[module.ctx_index] : NULL)

#define ngx_ngim_conf_get_module_main_conf(cf, module)                       \
    ((ngx_ngim_conf_ctx_t *) cf->ctx)->main_conf[module.ctx_index]
#define ngx_ngim_conf_get_module_srv_conf(cf, module)                        \
    ((ngx_ngim_conf_ctx_t *) cf->ctx)->srv_conf[module.ctx_index]
#define ngx_ngim_conf_get_module_task_conf(cf, module)                        \
    ((ngx_ngim_conf_ctx_t *) cf->ctx)->task_conf[module.ctx_index]

#define ngx_ngim_rcpymem(dst, src, n) \
    (((u_char*)ngx_ngim_rmemcpy(dst, src, n)) + (n))


/* enum */
enum {
  NGX_NGIM_SESSION_INIT,
  NGX_NGIM_SESSION_READ_REQUEST,
  NGX_NGIM_SESSION_PREPARE_REQUEST,
  NGX_NGIM_SESSION_DO_REQUEST,
  NGX_NGIM_SESSION_PREPARE_RESPONSE,
  NGX_NGIM_SESSION_DO_RESPONSE,
  NGX_NGIM_SESSION_FINALIZED,
  NGX_NGIM_SESSION_CLOSE_CONNECTION,
  NGX_NGIM_SESSION_MAX_STAGE
};

typedef enum{
  NGX_NGIM_PROTO_HTTP,
  NGX_NGIM_PROTO_TCP,
  NGX_NGIM_PROTO_UDP,
  NGX_NGIM_PROTO_MAX
} ngx_ngim_protocol_t;


/* struct */
typedef struct {
    void                  **main_conf;
    void                  **srv_conf;
    void                  **task_conf;
} ngx_ngim_conf_ctx_t;


typedef struct {
    u_char                  sockaddr[NGX_SOCKADDRLEN];
    socklen_t               socklen;

    /* server ctx */
    ngx_ngim_conf_ctx_t    *ctx;

    unsigned                bind:1;
    unsigned                wildcard:1;
#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
    unsigned                ipv6only:2;
#endif
    unsigned                so_keepalive:2;
#if (NGX_HAVE_KEEPALIVE_TUNABLE)
    int                     tcp_keepidle;
    int                     tcp_keepintvl;
    int                     tcp_keepcnt;
#endif
} ngx_ngim_listen_t;


typedef struct {
    ngx_ngim_conf_ctx_t    *ctx;
    ngx_str_t               addr_text;
} ngx_ngim_addr_conf_t;

typedef struct {
    in_addr_t               addr;
    ngx_ngim_addr_conf_t    conf;
} ngx_ngim_in_addr_t;


#if (NGX_HAVE_INET6)

typedef struct {
    struct in6_addr         addr6;
    ngx_ngim_addr_conf_t    conf;
} ngx_ngim_in6_addr_t;

#endif


typedef struct {
    void                   *addrs;
    ngx_uint_t              naddrs;
} ngx_ngim_port_t;


typedef struct {
    int                     family;
    in_port_t               port;
    ngx_array_t             addrs;       /* array of ngx_ngim_conf_addr_t */
} ngx_ngim_conf_port_t;


typedef struct {
    struct sockaddr        *sockaddr;
    socklen_t               socklen;

    ngx_ngim_conf_ctx_t    *ctx;

    unsigned                bind:1;
    unsigned                wildcard:1;
#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
    unsigned                ipv6only:2;
#endif
    unsigned                so_keepalive:2;
#if (NGX_HAVE_KEEPALIVE_TUNABLE)
    int                     tcp_keepidle;
    int                     tcp_keepintvl;
    int                     tcp_keepcnt;
#endif
} ngx_ngim_conf_addr_t;

typedef struct {
    ngx_str_t               name;
    void                  **ctx;
    void                  **main_conf;
    void                  **srv_conf;
    void                  **task_conf;

    ngx_pool_t             *pool;
    ngx_connection_t       *connection;

    ngx_event_t             close;

    cJSON                  *root;
    ngx_chain_t            *out;
    u_char                 *buff;

    size_t                  limit_rate;
    time_t                  start_sec;

    ngx_str_t              *addr_text;

    ngx_msec_t              timeout;
    ngx_msec_t              epoch;

    ngx_http_request_t     *request;

    unsigned                protocol:4;
    unsigned                stage:4;
    unsigned                async:4;
} ngx_ngim_session_t;

typedef struct {
    ngx_array_t             servers;    /* ngx_ngim_core_srv_conf_t */
    ngx_array_t             listen;     /* ngx_ngim_listen_t */

    ngx_array_t             events[NGX_NGIM_SESSION_MAX_STAGE];

    CURLM                  *curl_multi_handler;
    int                     easy_handlers;
    ngx_int_t               easy_handler_errs;

    /*curl_multi_timer */
    ngx_event_t             curl_multi_timer_evt;

    ngx_log_t              *log;

} ngx_ngim_core_main_conf_t;



typedef struct ngx_ngim_core_srv_conf_s {
    ngx_array_t             tasks; /* ngx_ngim_core_task_conf_t */

    ngx_flag_t              so_keepalive;
    ngx_msec_t              timeout;
    ngx_msec_t              ping_timeout;
    ngx_pool_t             *pool;
    ngx_ngim_conf_ctx_t    *ctx;
} ngx_ngim_core_srv_conf_t;

typedef struct {
    ngx_array_t             tasks;  /* ngx_ngim_core_task_conf_t */
    ngx_str_t               name;

    ngx_uint_t              which;  /* all rnd idx */
    ngx_uint_t              async;  /* sync async async_no */

    void                  **task_conf;
} ngx_ngim_core_task_conf_t;


typedef struct {
    ngx_str_t              *client;
    ngx_ngim_session_t     *session;
} ngx_ngim_log_ctx_t;


typedef struct {
    ngx_int_t             (*preconfiguration)(ngx_conf_t *cf);
    ngx_int_t             (*postconfiguration)(ngx_conf_t *cf);

    void                 *(*create_main_conf)(ngx_conf_t *cf);
    char                 *(*init_main_conf)(ngx_conf_t *cf, void *conf);

    void                 *(*create_srv_conf)(ngx_conf_t *cf);
    char                 *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, 
                                    void *conf);

    void                 *(*create_task_conf)(ngx_conf_t *cf);
    char                 *(*merge_task_conf)(ngx_conf_t *cf, void *prev,
                                    void *conf);
} ngx_ngim_module_t;


/* method */
/* Bit reverse: we need big-endians in many places  */
void               *ngx_ngim_rmemcpy(void *dst, const void* src, size_t n);

void                ngx_ngim_writer(ngx_event_t *wev);
void                ngx_ngim_empty_handler(ngx_event_t *wev);
ngx_int_t           ngx_ngim_write_filter(ngx_ngim_session_t *s,ngx_chain_t *in);
ngx_int_t           ngx_ngim_set_write_handler(ngx_ngim_session_t *s,ngx_int_t send_lowat);


void                ngx_ngim_init_connection(ngx_connection_t *c);
ngx_connection_t   *ngx_ngim_create_connection(curl_socket_t sock , ngx_log_t *log);
void                ngx_ngim_free_connection(ngx_connection_t *c);
void                ngx_ngim_close_connection(ngx_connection_t *c);

ngx_ngim_session_t *ngx_ngim_init_session(ngx_connection_t *c, ngx_ngim_addr_conf_t *addr_conf);
void                ngx_ngim_run_session(ngx_ngim_session_t *s); //cycle
void                ngx_ngim_close_session(ngx_ngim_session_t *s);
void                ngx_ngim_finalize_async_session(ngx_ngim_session_t *s);
void                ngx_ngim_finalize_session(ngx_ngim_session_t *s);

ngx_int_t           ngx_ngim_fire_event(ngx_ngim_session_t *s, ngx_uint_t evt,ngx_chain_t *in,void *data);

void                ngx_ngim_async_close_connection(ngx_connection_t *c);


/* method ptr */
/* handler result code:
 *  NGX_ERROR - error
 *  NGX_OK    - success, may continue
 *  NGX_DONE  - success, input parsed, reply sent; need no
 *      more calls on this event */    
typedef ngx_int_t (*ngx_ngim_stage_handler_pt)(ngx_ngim_session_t *s,ngx_chain_t *in,void *data);

typedef ngx_int_t (*ngx_ngim_top_body_filter_pt)(ngx_ngim_session_t *s,ngx_chain_t *in);

typedef ngx_int_t (*ngx_ngim_request_has_done_pt)(ngx_ngim_session_t *s,ngx_int_t proto,ngx_uint_t ctx_stage);

typedef ngx_int_t (*ngx_ngim_request_is_async_pt)(ngx_ngim_session_t *s,ngx_int_t proto);


/*typedef struct ngx_ngim_task_s  ngx_ngim_task_t;
typedef struct ngx_ngim_easyinfo_s  ngx_ngim_easyinfo_t;
typedef struct ngx_ngim_easy_sockinfo_s ngx_ngim_easy_sockinfo_t;

typedef ngx_int_t  (*ngx_ngim_task_init_pt)(ngx_ngim_task_t *task);
typedef ngx_int_t  (*ngx_ngim_task_req_done_pt)(void *);
typedef ngx_int_t  (*ngx_ngim_task_done_pt)(ngx_ngim_task_t *task);
*/


/* inline method */
static inline uint16_t
ngx_ngim_r16(uint16_t n)
{
    return (n << 8) | (n >> 8);
}


static inline uint32_t
ngx_ngim_r32(uint32_t n)
{
    return (n << 24) | ((n << 8) & 0xff0000) | ((n >> 8) & 0xff00) | (n >> 24);
}


static inline uint64_t
ngx_ngim_r64(uint64_t n)
{
    return (uint64_t) ngx_ngim_r32(n) << 32 | ngx_ngim_r32(n >> 32);
}

/* extern */

extern ngx_uint_t                           ngx_ngim_max_module;
extern ngx_module_t                         ngx_ngim_core_module;
extern ngx_ngim_core_main_conf_t           *ngx_ngim_core_main_conf;

/* var */
ngx_ngim_top_body_filter_pt  ngx_ngim_top_body_filter;

ngx_ngim_request_has_done_pt ngx_ngim_request_has_done;

ngx_ngim_request_is_async_pt ngx_ngim_request_is_async;

#endif /* _NGX_NGIM_H_INCLUDED_ */
